package de.negativrekord.blockworld.camera;


import cc.creativecomputing.CCApp;
import cc.creativecomputing.control.CCControl;
import cc.creativecomputing.graphics.CCCamera;
import cc.creativecomputing.graphics.CCGraphics;
import cc.creativecomputing.graphics.CCViewport;
import cc.creativecomputing.math.CCMath;
import cc.creativecomputing.math.CCVecMath;
import cc.creativecomputing.math.CCVector3f;
import cc.creativecomputing.math.spline.CCCatmulRomSpline;
import cc.creativecomputing.math.spline.CCSpline;
import de.negativrekord.blockworld.MultiPathGenerator;
import de.negativrekord.blockworld.MultiPathGenerator.PathStyle;

public class CameraWrapper2 {

	private CCSpline _myCloseUpPath;
	private CCSpline _myMidPath;
	private CCSpline _myFarPath;
	
	public enum Path {
		CLOSE,
		MID,
		FAR
	};
	
	private CCCamera _myCamera;
	protected CCVector3f currentPosition = new CCVector3f(0,0,0);
	private CCGraphics g;
	private int[][] heightMap;
	int mapSize;
	
	int w;
	int h;
	int nCloseupPositions = 20;
	
	
	
	public CameraWrapper2 (CCApp theApp, float split, float offset, int theSize, int[][] theHeightMap) {
		g = theApp.g;
		mapSize = theSize;
		heightMap = theHeightMap;
		_myCamera = new CCCamera(g);
		
		w = (int)(theApp.width*split);
		h = (int)(theApp.height);
		int x = (int)(w*offset);
		int y = 0;
		_myCamera.viewport(new CCViewport(x, y, w, h));
		_myCamera.near(1);
		_myCamera.fov(CCMath.radians(20));
		_myCamera.far(10000f);

		_myCloseUpPath = MultiPathGenerator.getCloseupPath(20, heightMap, mapSize);
		_myMidPath = MultiPathGenerator.getPath(PathStyle.SPHERE, 800, 0, new CCVector3f(0,0,0));
		_myFarPath = MultiPathGenerator.getPath(PathStyle.CIRCLE, 1200, 1f, new CCVector3f(1000,200,0),1000);
	}
	
	public void setHeightMap (int[][] theHeightMap) {
		heightMap = theHeightMap;
	}
	
	public CCCamera camera() { 
		return _myCamera;
	}
	
	public void setOffset (float theOffset) {
		int x = (int)(w*theOffset);
		int y = 0;
		_myCamera.viewport(new CCViewport(x, y, w, h));
	}
	
	public float sample (float val) {
		return ((int)(val*1000)) / 1000f;
	}
	
	int frameCount = 0;
	CCVector3f pos = new CCVector3f();
	CCVector3f target = new CCVector3f();
	float tCloseInt = 0f;
	
	private void offset(float theOffset){
		CCVector3f myOffsetAdd = CCVecMath.subtract(currentPosition, camera().target()).normalize(theOffset);
		camera().position(CCVecMath.add(currentPosition, myOffsetAdd));
	}
	
	public void updateFar(float tFar, float theOffset){
		currentPosition = _myFarPath.interpolate(tFar);
		_myCamera.position(currentPosition);
		_myCamera.target(500,400,0);
		_myCamera.up(new CCVector3f(0,1,0));
		
		offset(theOffset);
	}
	
	public void updateMid(float tMid, float theOffset){
		CCVector3f up = new CCVector3f(currentPosition);
		currentPosition = _myMidPath.interpolate(tMid);
		up.subtract(currentPosition);
		_myCamera.position(currentPosition);
		_myCamera.target(0,0,0);
		up.normalize();
		offset(theOffset);

		_myCamera.up(up);
	}
	
	float phase = 0f;
	float rotationSpeed = 0f;
	float squareSize = 0; //mapSize / 7f;
	
	public void updateClose(float tClose, float theMoveSpeed, float theLookSpeed, CCVector3f theOffset, float lookAngle) {
		
		float hOffset = 0;
		squareSize = mapSize / 7f;
		if (tClose%1f < tCloseInt || currentPosition == null) {
			int N = heightMap.length;
			
			int i = (int)CCMath.round(CCMath.random(0,6));
			int j = (int) CCMath.round(CCMath.random(0,6));
			currentPosition = new CCVector3f(heightMap[i+N/2][j+N/2],-mapSize/2 + i*mapSize/7,-mapSize/2 + j*mapSize/7);
//			if (h>10) hOffset = -10;
			
			CCVector3f up = new CCVector3f(0,1,0);
			phase = CCMath.random(0,CCMath.TWO_PI);
			rotationSpeed = CCMath.random(-20,20);
		}
		
		float c = squareSize/4*CCMath.cos(frameCount*0.1f*theMoveSpeed);
		float s = squareSize/4*CCMath.sin(frameCount*0.1f*theMoveSpeed);
		CCVector3f offset = new CCVector3f(hOffset, c, s);
		offset.add(theOffset);
		
		_myCamera.position (new CCVector3f(currentPosition).add(offset));
		_myCamera.target(
			1000 * lookAngle, 
			1000 * CCMath.cos(rotationSpeed * tClose * 0.1f * theLookSpeed + phase),
			1000 * CCMath.sin(rotationSpeed * tClose * 0.1f * theLookSpeed + phase)
		);
		_myCamera.up(new CCVector3f(1, 0, 0));
		
		tCloseInt = tClose % 1f;
		frameCount += 1;

		/*
		_myCamera.position(new CCVector3f(pos).add(add));
		_myCamera.target(0,0,0);
		_myCamera.up(up);
		*/
		
		//offset(theOffset);
	}
	
	private float _myDirX;
	private float _myDirY;
	
	public void updateStraight(float tClose, float theMoveSpeed, CCVector3f theOffset, float lookAngle) {
		
		float hOffset = 0;
		squareSize = mapSize / 7f;
		
		
		if (tClose%1f < tCloseInt || currentPosition == null) {
			int N = heightMap.length;
			
			float phase = CCMath.random(0,CCMath.TWO_PI);
			_myDirX = CCMath.cos(phase);
			_myDirY = CCMath.sin(phase);
			
			int i = 3;
			int j = 3;
			currentPosition = new CCVector3f(heightMap[i+N/2][j+N/2],-mapSize/2 + i*mapSize/7 - _myDirX,-mapSize/2 + j*mapSize/7 -_myDirY);
			frameCount = 0;
		}
		
		float c = _myDirX * frameCount * theMoveSpeed;
		float s = _myDirY * frameCount * theMoveSpeed;
		CCVector3f offset = new CCVector3f(hOffset, c, s);
		offset.add(theOffset);
		
		
		_myCamera.position (new CCVector3f(currentPosition).add(offset));
		_myCamera.target (0, _myCamera.position().y * lookAngle * 100, _myCamera.position().z );
		_myCamera.up(new CCVector3f(0,1,0));
		
		tCloseInt = tClose % 1f;
		frameCount += 1;
	}
	
	public void drawDebug(Path thePath){
		switch(thePath){
		case FAR:
			_myFarPath.draw(g);
			break;
		case MID:
			_myMidPath.draw(g);
			break;
		case CLOSE:
		
		}
		
		
		g.pushMatrix();
		g.pushAttribute();
		g.color(1f,0.5f,1f);
		g.translate(_myCamera.position());
		g.box(20);
		g.popAttribute();
		g.popMatrix();
		
		g.line(_myCamera.position(), _myCamera.target());
	}
	
	public void draw() {
		_myCamera.draw(g);
	}
}
